package top.lyooo.mybatislite.bean;

import top.lyooo.mybatislite.Annotation.DbField;
import top.lyooo.mybatislite.Annotation.DbPrimary;
import top.lyooo.mybatislite.Annotation.DbTable;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author :LiYu
 * @date :2020-08-25 17:02
 * @desc :
 * @return :
 */
public class SqlStatement {
    private String sql = "";
    private Map params;

    public Map getParams() {
        return params;
    }

    private void setParams(String param) {
        if (this.params == null) {
            HashMap<String, String> map = new HashMap<String, String>();
            map.put(param, param);
            this.params = map;
        } else {
            this.params.put(param, param);
        }
    }

    public static SqlStatement set() {
        SqlStatement sqlStatement = new SqlStatement();
        return sqlStatement;
    }

    public String get() {
        return sql;
    }

    @Override
    public String toString() {
        return sql;
    }

    /**
     * @return select... from ...
     */
    public static SqlStatement selectAll(Class<?> bean) {
        SqlStatement sqlStatement = new SqlStatement();
        String tableName = null;
        sqlStatement.sql = sqlStatement.sql + "select * from ";
        if (bean.isAnnotationPresent(DbTable.class)) {
            tableName = bean.getAnnotation(DbTable.class).value();
        } else {
            tableName = bean.getSimpleName();
        }
        sqlStatement.sql = sqlStatement.sql + tableName + " ";
        return sqlStatement;
    }

    public static SqlStatement selectCondition(Object bean) {
        SqlStatement sqlStatement = new SqlStatement();
        Class<?> bCls = bean.getClass();
        String tableName = null;
        String fieldName = null;
        if (bCls.isAnnotationPresent(DbTable.class)) {
            tableName = bCls.getAnnotation(DbTable.class).value();
        } else {
            tableName = bCls.getSimpleName();
        }
        sqlStatement.sql = sqlStatement.sql + "select * from " + tableName + " where ";
        Field[] df = bCls.getDeclaredFields();

        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbField.class)) {
                fieldName = field.getAnnotation(DbField.class).value();
            } else {
                fieldName = field.getName();
            }
            String fieldValue = null;
            try {
                if (field.get(bean) != null) {
                    if (field.get(bean).equals(0)) {
                        continue;
                    }
                    fieldValue = field.get(bean).toString();
                    sqlStatement.sql = sqlStatement.sql + fieldName + " = '" + fieldValue + "' and ";
                }

            } catch (IllegalAccessException e) {
                continue;
            }
        }
        sqlStatement.sql = sqlStatement.sql.substring(0, sqlStatement.sql.length() - 4) + " ";
        return sqlStatement;
    }

    /**
     * @return select *
     */
    public SqlStatement select() {
        this.sql = this.sql + "select * ";
        return this;
    }

    public SqlStatement select(Class<?> bean) {
        String fieldName = null;
        Field[] df = bean.getDeclaredFields();
        this.sql = this.sql + "select ";
        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbField.class)) {
                fieldName = field.getAnnotation(DbField.class).value();
            } else {
                fieldName = field.getName();
            }
            this.sql = this.sql + fieldName + ",";
        }
        this.sql = this.sql.substring(0, this.sql.length() - 1) + " ";
        return this;
    }

    /**
     * @return select(select...)...
     */
    public SqlStatement select(SqlStatement subSqlStatement) {
        if (subSqlStatement != null) {
            this.sql = this.sql + "select(" + subSqlStatement.toString() + ") ";
            this.setParams("1");
            this.params.putAll(subSqlStatement.getParams());
        }
        return this;
    }

    /**
     * @return select ...
     */
    public SqlStatement selectMath(String statement) {
        if (statement != null) {
            this.sql = this.sql + "select " + statement + " ";
        }
        return this;
    }

    /**
     * @return select ...,...
     */
    public SqlStatement select(String... params) {
        if (params != null) {
            this.sql = this.sql + "select ";
            for (int i = 0; i < params.length; i++) {
                this.sql = this.sql + (params[i].contains(".") ? params[i].replace("`", "").replace(".", ".`") : "`" + params[i].replace("`", "")) + "`,";
            }
            this.sql = this.sql.substring(0, this.sql.length() - 1) + " ";
        }
        return this;
    }

    /**
     * @return select ...,...
     */
    public SqlStatement selectDistinct(String... params) {
        if (params != null) {
            this.sql = this.sql + "select distinct ";
            for (int i = 0; i < params.length; i++) {
                this.sql = this.sql + (params[i].contains(".") ? params[i].replace("`", "").replace(".", ".`") : "`" + params[i].replace("`", "")) + "`,";
            }
            this.sql = this.sql.substring(0, this.sql.length() - 1) + " ";
        }
        return this;
    }

    /**
     * @return from ...
     */
    public SqlStatement from(String param) {
        if (param != null) {
            this.sql = this.sql + "from `" + (param.contains(" ") ? param.replace("`", "").replace(" ", "` ") : param.replace("`", "") + "` ") + " ";
        }
        return this;
    }

    /**
     * @return from (...)
     */
    public <T> SqlStatement from(Class<T> bean) {
        String tableName = null;
        this.sql = this.sql + "from ";
        if (bean.isAnnotationPresent(DbTable.class)) {
            tableName = bean.getAnnotation(DbTable.class).value();
        } else {
            tableName = bean.getSimpleName();
        }
        this.sql = this.sql + tableName + " ";
        return this;
    }

    /**
     * @return from (...)
     */
    public SqlStatement from(SqlStatement sqlStatement) {
        if (sqlStatement != null) {
            this.sql = this.sql + "from (" + sqlStatement + ") ";
            this.setParams("1");
            this.params.putAll(sqlStatement.getParams());
        }
        return this;
    }

    /**
     * @return join ... on ...
     */
    public SqlStatement join(String param, String condition) {
        if (param != null && condition != null) {
            this.sql = this.sql + "join `" + (param.contains(" ") ? param.replace("`", "").replace(" ", "` ") : param.replace("`", "") + "` ") + " on " + condition + " ";
        }
        return this;
    }

    /**
     * @return natural join ... on ...
     */
    public SqlStatement join(String param) {
        if (param != null) {
            this.sql = this.sql + "natural join `" + (param.contains(" ") ? param.replace("`", "").replace(" ", "` ") : param.replace("`", "") + "` ") + " ";
        }
        return this;
    }

    /**
     * @return where ...=...
     */
    public SqlStatement where(String key, Object value) {
        if (key != null && value != null) {
            this.sql = this.sql + "where " + (key.contains(".") ? key.replace("`", "")
                    .replace(".", ".`") : "`" + key.replace("`", "")) + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());

        }
        return this;
    }

    /**
     * @return where ...= (select...)
     */
    public SqlStatement where(String condition, SqlStatement subSqlStatement) {
        if (condition != null && subSqlStatement != null) {
            this.sql = this.sql + "where " + condition + " (" + subSqlStatement.toString() + ") ";
            this.setParams("1");
            this.params.putAll(subSqlStatement.getParams());
        }
        return this;
    }

    /**
     * @return where ...= (select...)
     */
    public SqlStatement where(SqlStatement subSqlStatement, String condition) {
        if (condition != null && subSqlStatement != null) {
            this.sql = this.sql + "where " + " (" + subSqlStatement.toString()+ ") " + condition + " ";
            this.setParams("1");
            this.params.putAll(subSqlStatement.getParams());
        }
        return this;
    }

    /**
     * @return where ...and...
     */
    public SqlStatement wheres(Object... k_v) {
        if (k_v != null) {
            this.sql = this.sql + "where ";
            for (int i = 0; i < k_v.length; i++) {
                if (i % 2 == 0) {
                    this.sql = this.sql + (k_v[i].toString().contains(".") ? k_v[i].toString().replace("`", "").replace(".", ".`") : "`" + k_v[i].toString().replace("`", "")) + "` = #{params.";
                } else {
                    this.sql = this.sql + k_v[i].toString() + "} and ";
                    this.setParams(k_v[i].toString());
                }
            }
            this.sql = this.sql.substring(0, this.sql.length() - 4) + " ";
        }
        return this;
    }

    /**
     * @return where ...
     */
    public SqlStatement whereMath(String statement) {
        if (statement != null) {
            this.sql = this.sql + "where " + statement + " ";
        }
        return this;
    }

    /**
     * @return or...=...
     */
    public SqlStatement or(String key, Object value) {
        if (key != null && value != null) {
            this.sql = this.sql + "or " + (key.contains(".") ? key.replace("`", "").replace(".", ".`") : "`" + key.replace("`", "")) + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return or
     */
    public SqlStatement or() {
        this.sql = this.sql + " or ";

        return this;
    }

    /**
     * @return and
     */
    public SqlStatement and() {
        this.sql = this.sql + " and ";

        return this;
    }

    /**
     * @return and...=....
     */
    public SqlStatement and(String key, Object value) {
        if (key != null && value != null) {
            this.sql = this.sql + "and " + (key.contains(".") ? key.replace("`", "").replace(".", ".`") : "`" + key.replace("`", "")) + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return gt...
     */
    public SqlStatement gt(Object value) {
        if (value != null) {
            this.sql = this.sql + "> " + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return gt
     */
    public SqlStatement gt() {
        this.sql = this.sql + " > ";

        return this;
    }

    /**
     * @return lt...
     */
    public SqlStatement lt(Object value) {
        if (value != null) {
            this.sql = this.sql + "< " + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return lt
     */
    public SqlStatement lt() {
        this.sql = this.sql + " < ";

        return this;
    }

    /**
     * @return ...
     */
    public SqlStatement text(Object value) {
        if (value != null) {
            this.sql = this.sql + value.toString() + " ";
        }
        return this;
    }

    /**
     * @return limit..., ...
     */
    public SqlStatement limit(int page, int pageSize) {
        if (page > 0 && pageSize > 0) {
            this.sql = this.sql + "limit " + (page - 1) * pageSize + "," + pageSize + " ";
        }
        return this;
    }

    /**
     * @return group by ...,...
     */
    public SqlStatement group(String... params) {
        if (params != null) {
            this.sql = this.sql + "group by ";
            for (String param : params) {
                this.sql = this.sql + (param.contains(".") ? param.replace("`", "").replace(".", ".`") : "`" + param.replace("`", "")) + "`,";
                this.setParams(param);
            }
            this.sql = this.sql.substring(0, this.sql.length() - 1) + " ";
        }
        return this;
    }

    /**
     * @return order by ...,...
     */
    public SqlStatement order(String... params) {
        if (params != null) {
            this.sql = this.sql + "order by ";
            for (String param : params) {
                if (param.contains(".")) {
                    if (param.contains(" ")) {
                        this.sql = this.sql + param.replace("`", "").replace(".", ".`").replace(" ", "` ") + ",";
                    } else {
                        this.sql = this.sql + param.replace("`", "").replace(".", ".`") + "`,";
                    }
                } else {
                    if (param.contains(" ")) {
                        this.sql = this.sql + "`" + param.replace("`", "").replace(" ", "` ") + ",";
                    } else {
                        this.sql = this.sql + "`" + param.replace("`", "") + "`,";
                    }
                }
            }

            this.sql = this.sql.substring(0, this.sql.length() - 1) + " ";
        }
        return this;
    }


    /**
     * @return having ....
     */
    public SqlStatement having(String key, Object value) {
        if (key != null && value != null) {
            this.sql = this.sql + "having " + (key.contains(".") ? key.replace("`", "").replace(".", ".`") : "`" + key.replace("`", "")) + "` = #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return having ...
     */
    public SqlStatement havingMath(String statement) {
        if (statement != null) {
            this.sql = this.sql + "having " + statement + " ";
        }
        return this;
    }

    /**
     * @return having(select...)
     */
    public SqlStatement having(SqlStatement subSqlStatement) {
        if (subSqlStatement != null) {
            this.sql = this.sql + "having(" + subSqlStatement.toString() + ") ";
            this.setParams("1");
            this.params.putAll(subSqlStatement.getParams());
        }
        return this;
    }

    /**
     * @return all(..., ...)
     */
    public SqlStatement all(String... params) {
        if (params != null) {
            this.sql = this.sql + "all(#{params.";
            for (String param : params) {
                this.sql = this.sql + param + "},#{params.";
                this.setParams(param);
            }
            this.sql = this.sql.substring(0, this.sql.length() - 10) + "}) ";
        }
        return this;
    }

    /**
     * @return any(..., ...)
     */
    public SqlStatement any(String... params) {
        if (params != null) {
            this.sql = this.sql + "any(#{params.";
            for (String param : params) {
                this.sql = this.sql + param + "},#{params.";
                this.setParams(param);
            }
            this.sql = this.sql.substring(0, this.sql.length() - 10) + "}) ";
        }
        return this;
    }

    /**
     * @return between...and...
     */
    public SqlStatement between(Object x, Object y) {
        if (x != null && y != null) {
            this.sql = this.sql + "between #{params." + x.toString() + "} and #{params." + y.toString() + "} ";
            this.setParams(x.toString());
            this.setParams(y.toString());
        }
        return this;
    }

    /**
     * @return in(..., ...)
     */
    public SqlStatement in(String... params) {
        if (params != null) {
            this.sql = this.sql + "in(#{params.";
            for (String param : params) {
                this.sql = this.sql + param + "},#{params.";
                this.setParams(param);
            }
            this.sql = this.sql.substring(0, this.sql.length() - 10) + "}) ";
        }
        return this;
    }

    /**
     * @return ... like ....
     */
    public SqlStatement like(String key, Object value) {
        if (key != null && value != null) {
            this.sql = this.sql + "`" + (key.contains(".") ? key.replace("`", "").replace(".", ".`") : "`" + key.replace("`", "")) + "` like #{params." + value.toString() + "} ";
            this.setParams(value.toString());
        }
        return this;
    }

    /**
     * @return insert into...values (...,...)
     */
    public SqlStatement insert(String from, Object... params) {
        if (params != null) {
            this.sql = this.sql + "insert into `" + from.replace("`", "") + "` values (#{params.";
            for (Object param : params) {
                if (param != null) {
                    param="";
                }
                this.sql = this.sql + param.toString() + "},#{params.";
                this.setParams(param.toString());
            }
            this.sql = this.sql.substring(0, this.sql.length() - 10) + "}) ";
        }
        return this;
    }

    public static SqlStatement insertBean(Object bean) {
        SqlStatement sqlStatement = new SqlStatement();
        Class<?> bCls = bean.getClass();
        String tableName = null;

        if (bCls.isAnnotationPresent(DbTable.class)) {
            tableName = bCls.getAnnotation(DbTable.class).value();
        } else {
            tableName = bCls.getSimpleName();
        }
        sqlStatement.sql = sqlStatement.sql + "insert into " + tableName + " (";
        Field[] df = bCls.getDeclaredFields();
        for (Field field : df) {
            field.setAccessible(true);
            String fieldName = null;
            try {
                if (field.get(bean)==null){continue;}
            } catch (IllegalAccessException e) {
                continue;
            }
            if (field.isAnnotationPresent(DbField.class)) {
                fieldName = field.getAnnotation(DbField.class).value();
            } else {
                fieldName = field.getName();
            }
            sqlStatement.sql = sqlStatement.sql +"`"+ fieldName + "`,";

        }
        sqlStatement.sql = sqlStatement.sql.substring(0, sqlStatement.sql.length() - 2) + "`) values (";
        for (Field field : df) {
            field.setAccessible(true);
            String name = field.getName();
            try {
                if (field.get(bean)==null){continue;}
                sqlStatement.sql = sqlStatement.sql +"#{params."+ field.get(bean).toString() + "},";
                sqlStatement.setParams(field.get(bean).toString());
            } catch (IllegalAccessException e) {
                continue;
            }
        }
        sqlStatement.sql = sqlStatement.sql.substring(0, sqlStatement.sql.length() - 2) + "}) ";
        return sqlStatement;
    }

    /**
     * @return update... set...=...,...where...
     */
    public SqlStatement update(String from, String where_k, String where_v, Object... k_v) {
        if (k_v != null && from != null && where_k != null && where_v != null) {
            this.sql = this.sql + "update `" + from.replace("`", "") + "` set `";
            for (int i = 0; i < k_v.length; i++) {
                if (i % 2 == 0) {
                    this.sql = this.sql + k_v[i].toString().replace("`", "") + "` = #{params.";
                } else {
                    this.sql = this.sql + k_v[i].toString() + "},`";
                    this.setParams(k_v[i].toString());
                }
            }
            this.sql = this.sql.substring(0, this.sql.length() - 2) + " where `" + where_k.replace("`", "") + "` = #{params." + where_v + "}";
            this.setParams(where_v);
        }
        return this;
    }

    public static SqlStatement updateBean(Object bean) {
        SqlStatement sqlStatement = new SqlStatement();
        Class<?> bCls = bean.getClass();
        String tableName = null;
        String fieldName = null;
        if (bCls.isAnnotationPresent(DbTable.class)) {
            tableName = bCls.getAnnotation(DbTable.class).value();
        } else {
            tableName = bCls.getSimpleName();
        }
        sqlStatement.sql = sqlStatement.sql + "update " + tableName + " set ";
        Field[] df = bCls.getDeclaredFields();
        int primaryNum = 0;
        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbPrimary.class)) {
                try {
                    if (field.get(bean) == null) {
                        throw new RuntimeException("定义@primary注解的属性值不能为空");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                primaryNum++;
            }
        }
        switch (primaryNum) {
            case 0:
                throw new RuntimeException("必须定义@primary注解,并且只能定义一个@primary");
            case 1:
                break;
            default:
                throw new RuntimeException("不能定义多个@primary注解");
        }
        for (Field field : df) {
            field.setAccessible(true);
            if (!field.isAnnotationPresent(DbPrimary.class)) {
                if (field.isAnnotationPresent(DbField.class)) {
                    fieldName = field.getAnnotation(DbField.class).value();
                } else {
                    fieldName = field.getName();
                }
                String fieldValue = null;
                try {
                    if (field.get(bean) != null&&!field.get(bean).equals(0)) {
                        fieldValue = field.get(bean).toString();
                        sqlStatement.sql = sqlStatement.sql + fieldName + " = #{params." + fieldValue + "},";
                        sqlStatement.setParams(fieldValue);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        sqlStatement.sql = sqlStatement.sql.substring(0, sqlStatement.sql.length() - 1) + " ";
        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbPrimary.class)) {
                if (field.isAnnotationPresent(DbField.class)) {
                    fieldName = field.getAnnotation(DbField.class).value();
                } else {
                    fieldName = field.getName();
                }
                String fieldValue = null;
                try {
                    fieldValue = field.get(bean).toString();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                sqlStatement.sql = sqlStatement.sql + "where " + fieldName + " = #{params."+ fieldValue + "}";
                sqlStatement.setParams(fieldValue);
            }
        }
        return sqlStatement;
    }

    /**
     * @return delete from...where...
     */
    public SqlStatement delete(String from, String key, Object value) {
        if (from != null && key != null && key != null) {
            this.sql = this.sql + "delete from `" + from.replace("`", "") + "` where `" + key.replace("`", "") + "` = #{params." + value.toString() + "}";
            this.setParams(key);
            this.setParams(value.toString());
        }
        return this;
    }

    public static SqlStatement deleteBean(Object bean) {
        SqlStatement sqlStatement = new SqlStatement();
        Class<?> bCls = bean.getClass();
        String tableName = null;
        String fieldName = null;
        if (bCls.isAnnotationPresent(DbTable.class)) {
            tableName = bCls.getAnnotation(DbTable.class).value();
        } else {
            tableName = bCls.getSimpleName();
        }
        sqlStatement.sql = sqlStatement.sql + "delete from " + tableName + " where ";
        Field[] df = bCls.getDeclaredFields();
        int primaryNum = 0;
        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbPrimary.class)) {
                try {
                    if (field.get(bean) == null) {
                        throw new RuntimeException("定义@primary注解的属性值不能为空");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                primaryNum++;
            }
        }
        switch (primaryNum) {
            case 0:
                throw new RuntimeException("必须定义@primary注解,并且只能定义一个@primary");
            case 1:
                break;
            default:
                throw new RuntimeException("不能定义多个@primary注解");
        }

        for (Field field : df) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbPrimary.class)) {
                if (field.isAnnotationPresent(DbField.class)) {
                    fieldName = field.getAnnotation(DbField.class).value();
                } else {
                    fieldName = field.getName();
                }
                String fieldValue = null;
                try {
                    fieldValue = field.get(bean).toString();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                sqlStatement.sql = sqlStatement.sql + fieldName + " = #{params." + fieldValue + "}";
                sqlStatement.setParams(fieldValue);
            }
        }
        return sqlStatement;
    }
}
